﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using CommandLine;
using CommandLine.Parsing;
using CommandLine.Infrastructure;
using CommandLine.Text;
using GTServices.Entities;
using GTServices.Model.CoRoutine;
using System.IO;

namespace GTServices.CommandLine.Client
{
    class ApplicationMain
    {
        static GTServices.Model.GTApplication AppModel { get { return GTServices.Model.GTApplication.Instance; } } 
        static void Main(string[] args)
        {
            Console.WriteLine("");

            Console.WriteLine("");
            Console.WriteLine("Welcome to CS^2 CommandLine tool. Brought to you by Casaba Security LLC.");
            var options = new CommandLineOptions();
           
            var parser = new Parser((x) =>
            {
                x.CaseSensitive = true;
                x.IgnoreUnknownArguments = true;
                x.HelpWriter = Console.Out;
            });
            if (parser.ParseArgumentsStrict(args, options))
            {
                // consume Options instance properties
                if (options.ListOptions != null)
                {
                    HandleListOptions(options);
                }
                else
                {
                    if ((options.Languages != null || options.Issues != null))
                    {
                        HandleSearch(options);
                    }
                }
            }
#if DEBUG
            Console.Out.WriteLine("Hit return to Close"); 
            Console.ReadLine(); 
#endif
        }

        static void HandleListOptions(CommandLineOptions options)
        {
            Console.Out.WriteLine("List Mode - All other options will be ignored." + options.ListOptions);
            Console.Out.WriteLine("This could take a moment. Please wait..");
            Console.Out.WriteLine("");
            switch (options.ListOptions.ToLower())
            {
                case "categories":
                    PrintCategories(); 
                    break;
                case "languages":
                    PrintLanguages(); 
                    break;
            }
          
        }
        static void PrintLanguages()
        {
            List<Language> langs = AppModel.DataModel.Languages.ToList();
            foreach (Language l in langs)
            {
                Console.Out.WriteLine(l.Name);
            }
        }
        static void PrintCategories()
        {
            List<Category> categories = AppModel.DataModel.Categories.ToList();
            foreach (Category c in categories)
            {
                Console.Out.WriteLine(c.Name);
            }
        }

        static void HandleSearch(CommandLineOptions options)
        {
            List<Pattern> patterns = null;
            List<Category> categories = null;

            //First let's quickly get a tmp file and make sure our root directory is set. 

            if (String.IsNullOrWhiteSpace(options.RootSearchDirectory))
            {
                options.RootSearchDirectory = System.IO.Directory.GetCurrentDirectory(); 
            }

            if (String.IsNullOrWhiteSpace(options.OutFile))
            {
                //If we didn't supply an output file we just create a random file at our current directory. 
                string currDir = System.IO.Directory.GetCurrentDirectory();

                String fName = "Result_" + DateTime.Now.ToBinary() + ".xml";
                options.OutFile = currDir + "\\" + fName;
            }

            if (options.Issues == null)
            {
                patterns = AppModel.DataModel.Patterns.Where(x => options.Languages.Contains(x.Language.Name)).ToList();
                categories = patterns.SelectMany(x => x.Categories).Distinct().ToList();
            }
            else if (options.Issues != null && options.Languages != null)
            {
                foreach (string issue in options.Issues)
                {
                    categories = new List<Category>(); 
                    Category c = AppModel.DataModel.Categories.FirstOrDefault(x => x.Name.Contains(issue));
                    if (c != null)
                    {
                        categories.Add(c); 
                    }
                }

                patterns = categories.SelectMany(x => x.Patterns).Where(x => options.Languages.Contains(x.Language.Name)).ToList();
            }
            else if (options.Issues != null && options.Languages == null)
            {
                foreach (string issue in options.Issues)
                {
                    categories = new List<Category>();
                    Category i = AppModel.DataModel.Categories.FirstOrDefault(x => x.Name.Contains(issue));
                    if (i != null)
                    {
                        categories.Add(i);
                    }
                }
                patterns = categories.SelectMany(x => x.Patterns).ToList();
            }
            else
            {
                Console.Out.WriteLine("Invalid options combinations");
                return;
            }

            Console.Out.WriteLine("Prepairing to Search: " + options.RootSearchDirectory); 


            ExecuteSearchQuery(categories, patterns, options.RootSearchDirectory, options.OutFile);

            
        }
        static void ExportResult(GrepResult gr, String fileName)
        {
            if (gr != null)
            {
                try
                {
                    using (StreamWriter sw = new StreamWriter(File.Open(fileName, FileMode.Create)))
                    {
                        sw.Write(AppModel.DataModel.GetEntityAsXml(gr));
                    }
                }
                catch (Exception e)
                {
                    Console.WriteLine(e.Message);
                }
            }
            else
            {
                Console.Write("Null GrepResult Arugment to ExportResult. Not good. Could be a failed search?"); 
            }
        }
        public static void ExecuteSearchQuery(List<Category> categories, List<Pattern> patterns, String rootDir, String outFile)
        {
            try
            {
                GrepRequest gr = AppModel.GrepModel.GetGrepRequest(rootDir, patterns, null, categories, null);

                ExecuteDirectoryGrepRequestRoutine routine = new ExecuteDirectoryGrepRequestRoutine(gr);
                routine.OnNextDirectory += new ExecuteDirectoryGrepRequestRoutine.NextDirectory((x, y) =>
                {
                    Console.Out.WriteLine(x);
                });
                /*
                 ExecuteGrepRequestRoutine routine = new ExecuteGrepRequestRoutine(gr);
                  */
                routine.Completed += (x, y) =>
                {

                    Console.Out.WriteLine("Completed!!"); 

                    if (y.Error == null)
                    {
                        GrepResult result = routine.Result;

                        Console.Out.WriteLine("Result Count: " + result.MatchInfos.Count);

                        Console.Out.WriteLine("Writing Result File to: " + outFile);
                        ExportResult(result, outFile);

                        if(File.Exists(outFile)){
                            Console.Out.WriteLine("Result file written"); 
                        }else{
                            Console.Out.WriteLine("Failed to write outfile.. not sure what happend"); 
                        }
                    }
                    else
                    {
                        Console.Out.WriteLine("Error: " + y.Error);
                    }
                };

                routine.Execute(null);
            }
            catch (Exception e)
            {
                Console.Out.WriteLine(e.Message);
            }
        }
    }
}
